/*!
 * upload 文件上传组件
 * MIT Licensed
 */

layui.define('layer', function (exports) {
    "use strict";

    var $ = layui.$,
        layer = layui.layer,
        hint = layui.hint(),
        device = layui.device()

    //外部接口
    , upload = {
        config: {} //全局配置项

        //设置全局项
        ,
        set: function (options) {
            var that = this;
            that.config = $.extend({}, that.config, options);
            return that;
        }

        //事件
        ,
        on: function (events, callback) {
            return layui.onevent.call(this, MOD_NAME, events, callback);
        }
    }

    //操作当前实例
    , thisUpload = function () {
        var that = this;
        return {
            upload: function (files) {
                that.upload.call(that, files);
            },
            reload: function (options) {
                that.reload.call(that, options);
            },
            config: that.config
        }
    }

    //字符常量
    , MOD_NAME = 'upload', ELEM = 'layui-upload', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled'

    , ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form', ELEM_IFRAME = 'layui-upload-iframe', ELEM_CHOOSE = 'layui-upload-choose', ELEM_DRAG = 'layui-upload-drag'


    //构造器
    , Class = function (options) {
        var that = this;
        that.config = $.extend({}, that.config, upload.config, options);
        that.render();
    };

    //默认配置
    Class.prototype.config = {
        accept: 'images' //允许上传的文件类型：images/file/video/audio
            ,
        exts: '' //允许上传的文件后缀名
            ,
        auto: true //是否选完文件后自动上传
            ,
        bindAction: '' //手动上传触发的元素
            ,
        url: '' //上传地址
            ,
        field: 'file' //文件字段名
            ,
        acceptMime: '' //筛选出的文件类型，默认为所有文件
            ,
        method: 'post' //请求上传的 http 类型
            ,
        data: {} //请求上传的额外参数
        ,
        drag: true //是否允许拖拽上传
            ,
        size: 0 //文件限制大小，默认不限制
            ,
        number: 0 //允许同时上传的文件数，默认不限制
            ,
        multiple: false //是否允许多文件上传，不支持ie8-9
    };

    //初始渲染
    Class.prototype.render = function (options) {
        var that = this,
            options = that.config;

        options.elem = $(options.elem);
        options.bindAction = $(options.bindAction);

        that.file();
        that.events();
    };

    //追加文件域
    Class.prototype.file = function () {
        var that = this,
            options = that.config,
            elemFile = that.elemFile = $([
                '<input class="' + ELEM_FILE + '" type="file" accept="' + options.acceptMime + '" name="' + options.field + '"', (options.multiple ? ' multiple' : ''), '>'
            ].join('')),
            next = options.elem.next();

        if (next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)) {
            next.remove();
        }

        //包裹ie8/9容器
        if (device.ie && device.ie < 10) {
            options.elem.wrap('<div class="layui-upload-wrap"></div>');
        }

        that.isFile() ? (
            that.elemFile = options.elem, options.field = options.elem[0].name
        ) : options.elem.after(elemFile);

        //初始化ie8/9的Form域
        if (device.ie && device.ie < 10) {
            that.initIE();
        }
    };

    //ie8-9初始化
    Class.prototype.initIE = function () {
        var that = this,
            options = that.config,
            iframe = $('<iframe id="' + ELEM_IFRAME + '" class="' + ELEM_IFRAME + '" name="' + ELEM_IFRAME + '" frameborder="0"></iframe>'),
            elemForm = $(['<form target="' + ELEM_IFRAME + '" class="' + ELEM_FORM + '" method="post" key="set-mine" enctype="multipart/form-data" action="' + options.url + '">', '</form>'].join(''));

        //插入iframe    
        $('#' + ELEM_IFRAME)[0] || $('body').append(iframe);

        //包裹文件域
        if (!options.elem.next().hasClass(ELEM_FORM)) {
            that.elemFile.wrap(elemForm);

            //追加额外的参数
            options.elem.next('.' + ELEM_FORM).append(function () {
                var arr = [];
                layui.each(options.data, function (key, value) {
                    value = typeof value === 'function' ? value() : value;
                    arr.push('<input type="hidden" name="' + key + '" value="' + value + '">')
                });
                return arr.join('');
            }());
        }
    };

    //异常提示
    Class.prototype.msg = function (content) {
        return layui.layer.msg(content, {
            icon: 2,
            shift: 6
        });
    };

    //判断绑定元素是否为文件域本身
    Class.prototype.isFile = function () {
        var elem = this.config.elem[0];
        if (!elem) return;
        return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file'
    }

    //预读图片信息
    Class.prototype.preview = function (callback) {
        var that = this;
        if (window.FileReader) {
            layui.each(that.chooseFiles, function (index, file) {
                var reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = function () {
                    callback && callback(index, file, this.result);
                }
            });
        }
    };

    //执行上传
    Class.prototype.upload = function (files, type) {
        var that = this,
            options = that.config,
            elemFile = that.elemFile[0]

        //高级浏览器处理方式，支持跨域
        , ajaxSend = function () {
            var successful = 0,
                aborted = 0,
                items = files || that.files || that.chooseFiles || elemFile.files,
                allDone = function () { //多文件全部上传完毕的回调
                    if (options.multiple && successful + aborted === that.fileLength) {
                        typeof options.allDone === 'function' && options.allDone({
                            total: that.fileLength,
                            successful: successful,
                            aborted: aborted
                        });
                    }
                };
            layui.each(items, function (index, file) {
                var formData = new FormData();

                formData.append(options.field, file);

                //追加额外的参数
                layui.each(options.data, function (key, value) {
                    value = typeof value === 'function' ? value() : value;
                    formData.append(key, value);
                });
                allDone();
                //提交文件
                // var opts = {
                //   url: options.url
                //   ,type: 'post' //统一采用 post 上传
                //   ,data: formData
                //   ,contentType: false 
                //   ,processData: false
                //   ,dataType: 'json'
                //   ,headers: options.headers || {}
                //   //成功回调
                //   ,success: function(res){
                //     successful++;
                //     done(index, res);
                //     allDone();
                //   }
                //   //异常回调
                //   ,error: function(){
                //     aborted++;
                //     that.msg('请求上传接口出现异常');
                //     error(index);
                //     allDone();
                //   }
                // };
                // //进度条
                // if(typeof options.progress === 'function'){
                //   opts.xhr = function(){
                //     var xhr = $.ajaxSettings.xhr();
                //     //上传进度
                //     xhr.upload.addEventListener("progress", function (obj) {
                //       if(obj.lengthComputable){
                //         var percent = Math.floor((obj.loaded/obj.total)* 100); //百分比
                //         options.progress(percent, (options.item ? options.item[0] : options.elem[0]) , obj, index);
                //       }
                //     });
                //     return xhr;
                //   }
                // }
                // $.ajax(opts);
            });
        }

        //低版本IE处理方式，不支持跨域
        , iframeSend = function () {
            var iframe = $('#' + ELEM_IFRAME);

            that.elemFile.parent().submit();

            //获取响应信息
            clearInterval(Class.timer);
            Class.timer = setInterval(function () {
                var res, iframeBody = iframe.contents().find('body');
                try {
                    res = iframeBody.text();
                } catch (e) {
                    that.msg('获取上传后的响应信息出现异常');
                    clearInterval(Class.timer);
                    error();
                }
                if (res) {
                    clearInterval(Class.timer);
                    iframeBody.html('');
                    done(0, res);
                }
            }, 30);
        }

        //统一回调
        , done = function (index, res) {
            that.elemFile.next('.' + ELEM_CHOOSE).remove();
            elemFile.value = '';
            if (typeof res !== 'object') {
                try {
                    res = JSON.parse(res);
                } catch (e) {
                    res = {};
                    return that.msg('请对上传接口返回有效JSON');
                }
            }
            typeof options.done === 'function' && options.done(res, index || 0, function (files) {
                that.upload(files);
            });
        }

        //统一网络异常回调
        , error = function (index) {
            if (options.auto) {
                elemFile.value = '';
            }
            typeof options.error === 'function' && options.error(index || 0, function (files) {
                that.upload(files);
            });
        }

        , exts = options.exts, check, value = function () {
            var arr = [];
            layui.each(files || that.chooseFiles, function (i, item) {
                arr.push(item.name);
            });
            return arr;
        }()

        //回调返回的参数
        , args = {
            //预览
            preview: function (callback) {
                that.preview(callback);
            }
            //上传
                ,
            upload: function (index, file) {
                var thisFile = {};
                thisFile[index] = file;
                that.upload(thisFile);
            }
            //追加文件到队列
                ,
            pushFile: function () {
                that.files = that.files || {};
                layui.each(that.chooseFiles, function (index, item) {
                    that.files[index] = item;
                });
                return that.files;
            }
            //重置文件
                ,
            resetFile: function (index, file, filename) {
                var newFile = new File([file], filename);
                that.files = that.files || {};
                that.files[index] = newFile;
            }
        }

        //提交上传
        , send = function () {
            //选择文件的回调      
            if (type === 'choose' || options.auto) {
                options.choose && options.choose(args);
                if (type === 'choose') {
                    return;
                }
            }

            //上传前的回调 - 如果回调函数明确返回false，则停止上传(#pulls55)
            if (options.before && (options.before(args) === false)) return;

            //IE兼容处理
            if (device.ie) {
                return device.ie > 9 ? ajaxSend() : iframeSend();
            }

            ajaxSend();
        }

        //校验文件格式
        value = value.length === 0 ?
            ((elemFile.value.match(/[^\/\\]+\..+/g) || []) || '') :
            value;

        if (value.length === 0) return;

        switch (options.accept) {
            case 'file': //一般文件
                if (exts && !RegExp('\\w\\.(' + exts + ')$', 'i').test(escape(value))) {
                    that.msg('选择的文件中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            case 'video': //视频文件
                if (!RegExp('\\w\\.(' + (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') + ')$', 'i').test(escape(value))) {
                    that.msg('选择的视频中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            case 'audio': //音频文件
                if (!RegExp('\\w\\.(' + (exts || 'mp3|wav|mid') + ')$', 'i').test(escape(value))) {
                    that.msg('选择的音频中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            default: //图片文件
                layui.each(value, function (i, item) {
                    if (!RegExp('\\w\\.(' + (exts || 'jpg|png|gif|bmp|jpeg$') + ')', 'i').test(escape(item))) {
                        check = true;
                    }
                });
                if (check) {
                    that.msg('选择的图片中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
        }

        //检验文件数量
        that.fileLength = function () {
            var length = 0,
                items = files || that.files || that.chooseFiles || elemFile.files;
            layui.each(items, function () {
                length++;
            });
            return length;
        }();
        if (options.number && that.fileLength > options.number) {
            return that.msg('同时最多只能上传的数量为：' + options.number);
        }

        //检验文件大小
        if (options.size > 0 && !(device.ie && device.ie < 10)) {
            var limitSize;

            layui.each(that.chooseFiles, function (index, file) {
                if (file.size > 1024 * options.size) {
                    var size = options.size / 1024;
                    size = size >= 1 ? (size.toFixed(2) + 'MB') : options.size + 'KB'
                    elemFile.value = '';
                    limitSize = size;
                }
            });
            if (limitSize) return that.msg('文件不能超过' + limitSize);
        }
        send();
    };

    //重置方法
    Class.prototype.reload = function (options) {
        options = options || {};
        delete options.elem;
        delete options.bindAction;

        var that = this,
            options = that.config = $.extend({}, that.config, upload.config, options),
            next = options.elem.next();

        //更新文件域相关属性
        next.attr({
            name: options.name,
            accept: options.acceptMime,
            multiple: options.multiple
        });
    };

    //事件处理
    Class.prototype.events = function () {
        var that = this,
            options = that.config

        //设置当前选择的文件队列
        , setChooseFile = function (files) {
            that.chooseFiles = {};
            layui.each(files, function (i, item) {
                var time = new Date().getTime();
                that.chooseFiles[time + '-' + i] = item;
            });
        }

        //设置选择的文本
        , setChooseText = function (files, filename) {
            var elemFile = that.elemFile,
                item = options.item ? options.item : options.elem,
                value = files.length > 1 ?
                files.length + '个文件' :
                ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g) || []) || '');

            if (elemFile.next().hasClass(ELEM_CHOOSE)) {
                elemFile.next().remove();
            }
            that.upload(null, 'choose');
            if (that.isFile() || options.choose) return;
            elemFile.after('<span class="layui-inline ' + ELEM_CHOOSE + '">' + value + '</span>');
        };

        //点击上传容器
        options.elem.off('upload.start').on('upload.start', function () {
            var othis = $(this),
                data = othis.attr('lay-data');

            if (data) {
                try {
                    data = new Function('return ' + data)();
                    that.config = $.extend({}, options, data);
                } catch (e) {
                    hint.error('Upload element property lay-data configuration item has a syntax error: ' + data)
                }
            }

            that.config.item = othis;
            that.elemFile[0].click();
        });

        //拖拽上传
        if (!(device.ie && device.ie < 10)) {
            options.elem.off('upload.over').on('upload.over', function () {
                var othis = $(this)
                othis.attr('lay-over', '');
            })
                .off('upload.leave').on('upload.leave', function () {
                    var othis = $(this)
                    othis.removeAttr('lay-over');
                })
                .off('upload.drop').on('upload.drop', function (e, param) {
                    var othis = $(this),
                        files = param.originalEvent.dataTransfer.files || [];

                    othis.removeAttr('lay-over');
                    setChooseFile(files);

                    if (options.auto) {
                        that.upload(files);
                    } else {
                        setChooseText(files);
                    }
                });
        }

        //文件选择
        that.elemFile.off('upload.change').on('upload.change', function () {
            var files = this.files || [];
            setChooseFile(files);
            options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
        });

        //手动触发上传
        options.bindAction.off('upload.action').on('upload.action', function () {
            that.upload();
        });

        //防止事件重复绑定
        if (options.elem.data('haveEvents')) return;

        that.elemFile.on('change', function () {
            $(this).trigger('upload.change');
        });

        options.elem.on('click', function () {
            if (that.isFile()) return;
            $(this).trigger('upload.start');
        });

        if (options.drag) {
            options.elem.on('dragover', function (e) {
                e.preventDefault();
                $(this).trigger('upload.over');
            }).on('dragleave', function (e) {
                $(this).trigger('upload.leave');
            }).on('drop', function (e) {
                e.preventDefault();
                $(this).trigger('upload.drop', e);
            });
        }

        options.bindAction.on('click', function () {
            $(this).trigger('upload.action');
        });

        options.elem.data('haveEvents', true);
    };

    //核心入口  
    upload.render = function (options) {
        var inst = new Class(options);
        return thisUpload.call(inst);
    };

    exports(MOD_NAME, upload);
});